/**
 *用于替换系统的timer！
 *使用tag，这样更方便一些；设置相同tag的timer，会自动删除之前tag
 *重入的问题：
 *在timer中添加和删除timer，不会有问题
 *在timer中release自身，会等待timer结束才release——把内存还给pool
 */

// import { strict as assert } from 'assert';
// import { CostGuard, CostGuards } from './profile';
// import { Pool } from './Pool';
// import { runtimeError } from './log';
// import { Timex, getTimeStr } from "./timex";
// import { GameTime } from './GameTime';
// import { CallObject, EventCallbackFunc } from './CallObject';
// import { getTimeStr } from './TimeUtils';

const timerguards = new CostGuards('TimerGuards');
function getTimerGuard(): CostGuards {
	return timerguards;
}

type TimeoutCallbackFunc = () => void;
type TimeRepeatCallbackFunc = (called: number, repeat: number) => unknown;
type TimeRepeatCallbackChecrtlFunc = (called: number, repeat: number) => boolean;
type TimeCallbackFunc = TimeoutCallbackFunc | TimeRepeatCallbackFunc;

const westTimerName = 'westTimer';
// like egret.
class WestTimer extends CallObject {
	delay = 0;

	repeatCount = 0;

	// callend?:Function; 不再支持callend了，因为times通过call返回，所以很容易判断是否end
	handle?: any;

	checkReturn = false;

	autoRelease = false;

	timeoutFunc: TimeoutCallbackFunc;

	intervalFunc: TimeoutCallbackFunc;

	timeInit = 0;

	constructor() {
		super();

		// 闭包耗时，所以只做一次！
		this.timeoutFunc = this.onTimeout.bind(this);
		this.intervalFunc = this.onInterval.bind(this);
	}

	initTimer(
		name: string,
		delay: number,
		repeatCount: number,
		call: EventCallbackFunc,
		self: unknown,
		endRelease: boolean,
	): void {
		this.handle = null;

		this.initCall(name, call, self, timerguards);
		this.delay = delay < 0 ? 0 : delay;
		this.repeatCount = repeatCount;
		this.autoRelease = endRelease;
		assert(this.guard != null);
	}

	isStart(): boolean {
		return this.handle != null;
	}

	start(paused: boolean): boolean {
		if (this.isStart()) {
			console.warn('westTimer.start already this.handle');
			return false;
		}

		// this.timeInit = Timex.ins().now;
		this.timeInit = GameTime.ins().getTimems();
		if (!paused) {
			this.resume();
		}

		this.guard.running += 1;
		this.called = 0;
		return true;
	}

	stop(): boolean {
		if (!this.isStart()) {
			console.error('westTimer.stop !this.handle');
			return false;
		}
		if (this.repeatCount === 1) {
			clearTimeout(this.handle);
		} else {
			clearInterval(this.handle);
		}
		this.onEnd();
		return true;
	}

	onEnd(): void {
		this.handle = undefined;
		this.guard.running -= 1;
		if (this.autoRelease) this.release();
	}

	onInterval(): void {
		const rt = this.onTimer();
		// 必须返回false, null or undefiend 都不行
		if (this.called === this.repeatCount || (this.checkReturn && rt === false)) {
			this.stop();
		}
		this.checkRelease();
	}

	checkRelease(): void {
		if (this._reRelease) {
			this._reRelease = false;
			this.release();
		}
	}

	onTimeout(): void {
		this.onTimer();
		this.onEnd();
		this.checkRelease();
	}

	guard: CostGuard;

	private _onTimer = false;

	onTimer(): boolean {
		this._onTimer = true;
		const rt = this.onCallTimer(this.called, this.repeatCount);
		this._onTimer = false;
		return rt;
	}

	private _reRelease = false;

	release(): void {
		// 必须保证ontimer执行完毕最后才release，而不是简单判断is_release
		// 因为release之后，还有可能分配给了其他对象，再执行on_end或stop就错了！
		if (this._onTimer) {
			// console.warn('release when this.ontimer', this._name);
			this._reRelease = true;
			return;
		}

		// console.log("timer.release",this._name)

		if (this.handle) {
			this.autoRelease = false; // or stop will call release again!
			this.stop();
		}

		if (this.guard) {
			this.guard = undefined;
			Pool.ins().push(this);
		} else runtimeError(`westtimer.release twice.${this._name}`);
	}

	dump(running: boolean): void {
		console.log(
			`westTimer.dump ${this._name} status:${
				running ? 'running' : 'pool'
			} start:${this.isStart()}
            init:${getTimeStr(this.timeInit)} delay:${this.delay} called:${this.called}`,
		);
	}

	static dumpPool(): void {
		Pool.ins().dumpPool(westTimerName);
	}

	// 查看某个timer的使用！
	static dumpTimer(name: string): void {
		Pool.ins().dumpName(westTimerName, name);
	}

	pause(): void {
		if (this.repeatCount === 1) {
			clearTimeout(this.handle);
		} else {
			clearInterval(this.handle);
		}
		this.handle = null;
	}

	// resume重新设定timer，但是并不是精确的跟之前一致
	resume(): void {
		if (this.repeatCount === 1) {
			this.handle = setTimeout(this.timeoutFunc, this.delay);
		} else {
			this.handle = setInterval(this.intervalFunc, this.delay);
		}
	}
}

function westCreatetimer(
	name: string,
	delay: number,
	repeatCount: number,
	call: TimeCallbackFunc,
	self: unknown,
	endrelease: boolean,
): WestTimer {
	const timer: WestTimer = Pool.ins().pop(name, westTimerName, WestTimer);
	timer.initTimer(name, delay, repeatCount, call, self, endrelease);
	return timer;
}

// 都改为了自动析构的timer
function westSetTimeout(
	name: string,
	call: TimeoutCallbackFunc,
	self: unknown,
	ms: number,
): WestTimer {
	const timer = westCreatetimer(name, ms, 1, call, self, true);
	timer.start(false);
	return timer;
}

// 持续的timer无法自动析构
function westSetInterval(
	name: string,
	call: TimeRepeatCallbackFunc,
	self: unknown,
	ms: number,
): WestTimer {
	const timer = westCreatetimer(name, ms, -1, call, self, false);
	timer.start(false);
	return timer;
}

// timer返回false自动结束timer！
function westSetIntervalCheckrt(
	name: string,
	call: TimeRepeatCallbackChecrtlFunc,
	self: unknown,
	ms: number,
): WestTimer {
	const timer = westCreatetimer(name, ms, -1, call, self, true);
	timer.checkReturn = true;
	timer.start(false);
	return timer;
}

// repeatCount mean callback 0,1,...,repeatCount-1
function westSetTimerRepeat(
	name: string,
	ms: number,
	repeatCount: number,
	call: TimeRepeatCallbackFunc,
	self: unknown,
): WestTimer {
	const timer = westCreatetimer(name, ms, repeatCount, call, self, true);
	timer.start(false);
	return timer;
}

// will call 0,1,...,repeatCount并且立即调用call(0)
function westSetTimerRepeatNow(
	name: string,
	ms: number,
	repeatCount: number,
	call: TimeRepeatCallbackFunc,
	self: unknown,
): WestTimer {
	const timer = westCreatetimer(name, ms, repeatCount + 1, call, self, true);
	timer.start(false);
	timer.onTimer();
	return timer;
}

// 下一帧开始调用
function westNextframe(name: string, call: TimeoutCallbackFunc, self: unknown): WestTimer {
	const timer = westCreatetimer(name, 0, 1, call, self, true);
	timer.start(false);
	return timer;
}
